use std::time::Duration;

use anyhow::{bail, Result};
use async_h1::client::Encoder;
use async_h1::server::decode;
use async_std::future::{timeout, TimeoutError};
use async_std::io;
use async_std::net::{SocketAddr, TcpListener, TcpStream};
use async_std::task;
use async_tls::TlsConnector;

const TIMEOUT: Duration = Duration::from_secs(3);

fn main() {
    pretty_env_logger::init_timed();
    if let Err(e) = task::block_on(run()) {
        log::error!("run fail: {}", e)
    }
}

async fn run() -> Result<()> {
    let listener = TcpListener::bind("127.0.0.1:8081").await?;

    loop {
        let (stream, _) = listener.accept().await?;
        task::spawn(async move {
            if let Err(err) = handle(stream).await {
                log::error!("handle fail: [{:?}]", err)
            }
        });
    }
}

async fn handle(mut proxy: TcpStream) -> Result<()> {
    let (mut req, mut body) = match timeout(TIMEOUT, decode(proxy.clone())).await {
        Ok(Ok(Some(r))) => r,
        Ok(Ok(None)) | Err(TimeoutError { .. }) => bail!("Timeout!"), /* EOF or timeout */
        Ok(Err(e)) => anyhow::bail!("{}", e),
    };
    io::copy(&mut body, &mut io::sink()).await?;

    let ip_port = req.header("ip_port").map(|h| h.as_str()).unwrap();
    log::info!("ip_port: {}", ip_port);
    let addrs = ip_port.split(':').collect::<Vec<&str>>();
    let socket = SocketAddr::new(addrs[0].parse().unwrap(), addrs[1].parse().unwrap());
    req.remove_header("ip_port");

    // let socket: SocketAddr = *req.ext().get().unwrap();
    // Connect remote and write http request to remote server
    // log::trace!("socket: {}", socket.to_string());

    let remote = TcpStream::connect(socket).await?;
    log::debug!("tcp connected. Prepare tls: {} ", req.host().unwrap());
    let mut tls_remote = TlsConnector::default()
        .connect(req.host().unwrap(), remote)
        .await?;

    log::debug!("send request");
    let mut req = Encoder::new(req);
    log::trace!("> {:?}", &req);

    io::copy(&mut req, &mut tls_remote).await?;
    io::copy(&mut tls_remote, &mut proxy).await?;

    // async_h1::accept(proxy, |mut req| async {
    //     //TODO 解密req
    //     log::info!("req: {:?}", req);
    //     get_socket_addr(req.clone())?;
    //
    //     let ip_port = req.header("ip_port").map(|h| h.as_str()).unwrap();
    //     log::info!("ip_port: {}", ip_port);
    //     let addrs = ip_port.split(':').collect::<Vec<&str>>();
    //     let socket = SocketAddr::new(addrs[0].parse().unwrap(), addrs[1].parse().unwrap());
    //     req.remove_header("ip_port");
    //
    //     // let socket: SocketAddr = *req.ext().get().unwrap();
    //     // Connect remote and write http request to remote server
    //     // log::trace!("socket: {}", socket.to_string());
    //
    //     let remote = TcpStream::connect(socket).await?;
    //     log::debug!("tcp connected. Prepare tls: {} ", req.host().unwrap());
    //     let tls_remote = TlsConnector::default()
    //         .connect(req.host().unwrap(), remote)
    //         .await?;
    //
    //     log::debug!("send request");
    //     let res = async_h1::connect(tls_remote, req).await?;
    //     log::debug!("receive response: {:#?}", res);
    //
    //     Ok(res)
    // })
    // .await
    // .map_err(|e| anyhow::anyhow!("{}", e))?;

    Ok(())
}

// fn get_socket_addr(req: Request) -> Result<SocketAddr> {
//     let mut port = match req.url().scheme() {
//         "https" => 443_u16,
//         _ => 80_u16,
//     };
//
//     let host = req
//         .url()
//         .host()
//         .map(|h| h.to_string())
//         .context("Host not found in request url")?;
//
//     if let Some(p) = req.header("port") {
//         port = p.to_string().parse()?;
//     }
//
//     let resolver = Resolver::new(ResolverConfig::default(), ResolverOpts::default())?;
//     let host_ips = resolver.lookup_ip(&*host)?;
//     for host_ip in host_ips.iter() {
//         log::info!("lookup_ip: {:?}", host_ip);
//     }
//
//     let host_ip: IpAddr = host_ips
//         .iter()
//         .next()
//         .context(format!("未解析到域名{}的ip", host))?;
//
//     Ok(SocketAddr::new(host_ip, port))
// }
